package com.dc.toolkit.guava.io;

import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.common.io.*;
import org.junit.After;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

/**
 * Description: Guava Files demo
 * @author: gdc
 * @date: 2021/4/24
 * @version 1.0
 */
public class FilesTest {

    public static final String SOURCE_FILE = "D:\\testFiles.txt";
    public static final String TARGET_FILE = "D:\\testFiles2.txt";


    /**
     * 使用 Guava 的 Files,完成对文件的 Copy
     */
    @Test
    public void testCopyFileWithGuava() throws IOException {
        File targetFile = new File(TARGET_FILE);
        Files.copy(new File(SOURCE_FILE), new File(TARGET_FILE));
        assertThat(targetFile.exists(), equalTo(true));

        // 文件 COPY 完成，比较 hash 值
        HashCode sourceHashCode = Files.asByteSource(new File(SOURCE_FILE)).hash(Hashing.sha256());
        HashCode targetHashCode = Files.asByteSource(targetFile).hash(Hashing.sha256());
        assertThat(sourceHashCode, equalTo(targetHashCode));
    }

    /**
     * 使用 JDK 的 Files,完成对文件的 Copy
     */
    @Test
    public void testCopyFileWithJdkNio() throws IOException {
        java.nio.file.Files.copy(
                Paths.get(SOURCE_FILE),
                Paths.get(TARGET_FILE),
                StandardCopyOption.REPLACE_EXISTING
        );
        assertThat(new File(TARGET_FILE).exists(), equalTo(true));
    }

    /**
     * move() 完成对文件的搬移
     * 细节：finally 中，将搬移后的数据，重新修改回来，保障测试可以反复执行
     */
    @Test
    public void testMoveFile() throws IOException {
        try {
            Files.move(new File(SOURCE_FILE), new File(TARGET_FILE));
            assertThat(new File(TARGET_FILE).exists(), equalTo(true));
            assertThat(new File(SOURCE_FILE).exists(), equalTo(false));
        } finally {
            Files.move(new File(TARGET_FILE), new File(SOURCE_FILE));
        }
    }

    /**
     * Files.readLines()        对取文件内容
     */
    @Test
    public void testToString() throws IOException {
        String expectedString = "1. 运行\n" +
                "在单元测试类上右键菜单“Run As” -> “2 PIT Mutation Test”，即可运行：\n" +
                "运行过程以及结果输出的窗口：\n" +
                "\n" +
                "2. 输出报告\n" +
                "可在此窗口中查看变异测试发现的可能存在的代码缺陷：（这点比IDEA的PIT插件做的要好）\n" +
                "可在此窗口中查看测试报告：";

        List<String> strings = Files.readLines(new File(SOURCE_FILE), Charsets.UTF_8);
        String result = Joiner.on("\n").join(strings);
        assertThat(result, equalTo(expectedString));
    }

    /**
     * 读取文件，处理文件内容
     */
    @Test
    public void testToProcessString() throws IOException {
        LineProcessor lineProcessor = new LineProcessor<List<Integer>>() {

            private List<Integer> lengthList = Lists.newArrayList();

            @Override
            public boolean processLine(String line) throws IOException {
                // 如果长度为 0 ，跳过统计
                if (line.length() == 0) {
                    return false;
                }

                // add 每一行的长度
                lengthList.add(line.length());
                return true;
            }

            @Override
            public List<Integer> getResult() {
                return lengthList;
            }
        };

        List<Integer> result = (List<Integer>) Files.asCharSource(new File(SOURCE_FILE), Charsets.UTF_8).readLines(lineProcessor);
        System.out.println(result);
    }

    /**
     * 获取文件的 Hash 值
     */
    @Test
    public void testFileSha256() throws IOException {
        File file = new File(SOURCE_FILE);
        HashCode hashCode = Files.asByteSource(file).hash(Hashing.sha256());
        System.out.println(hashCode);
    }

    /**
     * 文件写入 和 读取内容
     */
    @Test
    public void testFileWriteAndRead() throws IOException {
        File targetFile = new File(TARGET_FILE);
        targetFile.deleteOnExit();

        String writeContent = "abcdefghijklmnopqistuvwxyz";
        // 文件写入
        Files.asCharSink(targetFile, Charsets.UTF_8, FileWriteMode.APPEND).write(writeContent);

        // 文件读取
        String readContent = Files.asCharSource(targetFile, Charsets.UTF_8).read();
        assertThat(readContent, equalTo(writeContent));
    }

    /**
     * 文件写入追加
     */
    @Test
    public void testFileAppend() throws IOException {
        File file = new File(TARGET_FILE);
        file.deleteOnExit();

        String writeContent = "abcde";

        // 文件写入
        CharSink charSink = Files.asCharSink(file, Charsets.UTF_8, FileWriteMode.APPEND);
        charSink.write(writeContent);

        // 文件读取
        CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);
        String readContent = charSource.read();
        assertThat(readContent, equalTo(writeContent));

        // 文件写入（追加操作）
        String writeContent2 = "12345";
        charSink.write(writeContent2);

        // 文件读取
        String readContent2 = charSource.read();
        assertThat(readContent2, equalTo("abcde12345"));
    }

    /**
     * Files.touch()            创建一个空文件
     */
    @Test
    public void testToTouchFile() throws IOException {
        File file = new File(TARGET_FILE);
        file.deleteOnExit();

        Files.touch(file);
        assertThat(file.exists(), equalTo(true));
    }

    /**
     * 递归查询获取文件名称
     */
    @Test
    public void testRecursive(){
        ArrayList<File> list = Lists.newArrayList();
        recursiveList(new File("D:\\"), list);
        list.forEach(f -> System.out.println(f.getAbsolutePath()));
    }

    private void recursiveList(File root, List<File> fileList) {
        // 隐藏文件跳过
        if (root.isHidden()) {
            return;
        }

        if (!root.isFile()) {
            File[] files = root.listFiles();
            if (files.length > 0) {
                for (File file : files) {
                    recursiveList(file, fileList);
                }
            }
        } else {
            fileList.add(root);
        }
    }

    /**
     * 此处是保证每次测试之后的环境，和之前是一样的
     */
    @After
    public void tearDown(){
        File targetFile = new File(TARGET_FILE);
        if (targetFile.exists()) {
            targetFile.delete();
        }
    }
}
